package cnrpc

import (
	"context"
	"fmt"
	"time"

	// etcd
	eclient "go.etcd.io/etcd/client/v3"
	"go.etcd.io/etcd/client/v3/naming/endpoints"
)

type (
	Registrar interface {
		// 注册实例
		Register(ctx context.Context) error
		// 反注册实例
		Deregister(ctx context.Context) error
	}
	Discovery interface {
		// 根据 serviceName 直接拉取实例列表
		GetService(ctx context.Context, serviceName string) error
		// 根据 serviceName 阻塞式订阅一个服务的实例列表信息
		Watch(ctx context.Context, serviceName string) error
	}
)

type (
	Register struct {
		EtcdURL     string //etcd的访问地址
		ServiceName string //服务名称
		ServiceURL  string //服务地址
		Ttl         int64  //当前注册的存活时间
	}
)

func NewRegister(etcdURL, serviceName, serviceURL string, ttl int64) *Register {
	register := new(Register)
	register.EtcdURL = etcdURL
	register.ServiceName = serviceName
	register.ServiceURL = serviceURL
	register.Ttl = ttl
	return register
}

func (register *Register) Register(ctx context.Context) (err error) {
	//创建 etcd 客户端
	etcdClient, err := eclient.NewFromURL(register.EtcdURL)
	if err != nil {
		return
	}
	etcdManager, err := endpoints.NewManager(etcdClient, register.ServiceName)
	if err != nil {
		return
	}
	//创建一个租约，定时etcd 汇报一次心跳，证明当前节点仍然存活
	lease, err := etcdClient.Grant(ctx, register.Ttl)
	if err != nil {
		return
	}
	fmt.Println("lease id:", lease.ID)
	//添加注册节点到 etcd 中，并且携带上租约 id
	err = etcdManager.AddEndpoint(ctx, fmt.Sprintf("%s/%s", register.ServiceName, register.ServiceURL), endpoints.Endpoint{Addr: register.ServiceURL}, eclient.WithLease(lease.ID))
	if err != nil {
		return
	}
	//定时进行一次延续租约的动作，时间是存活时间的一半
	for {
		select {
		case <-time.After(time.Duration(register.Ttl / 2 * int64(time.Second))):
			resp, _ := etcdClient.KeepAliveOnce(ctx, lease.ID)
			fmt.Printf("keep alive resp: %+v \n", resp)
		case <-ctx.Done():
			return
		}
	}
}

func (register *Register) Deregister(ctx context.Context) (err error) {
	//创建 etcd 客户端
	etcdClient, err := eclient.NewFromURL(register.EtcdURL)
	if err != nil {
		return
	}
	etcdManager, err := endpoints.NewManager(etcdClient, register.ServiceName)
	if err != nil {
		return
	}
	//删除已经注册etcd的节点
	err = etcdManager.DeleteEndpoint(ctx, fmt.Sprintf("%s/%s", register.ServiceName, register.ServiceURL))
	return
}
